// -*-mode:c++; coding:utf-8-*-

#ifndef _KB_DETAILS_BINARY_ARCHIVE_BASE_
#define _KB_DETAILS_BINARY_ARCHIVE_BASE_

#include <unistd.h>		// for SEEK_*
#include <limits>

namespace kb
{
	namespace details
	{
		
		struct binary_archive_config_default
		{
			// 是否取消所有选项，该值会掩盖下面的所有选项值。
			// 全部取消后，基类将被优化为空类。
			static const bool disable_all = false;

			// ---
			
			// 是否启用数据大小检查。这会使读取基本数据类型
			// 时，都检查剩下的数据是否足够，若不够则马上返
			// 回false并置状态；否则总返回true。
			static const bool enable_check_data_bound = true;

			// 是否启用长度类型的overflow检查
			static const bool enable_check_size_limits = true;
		};

		struct binary_archive_config_debug
		{
			static const bool disable_all = false;

			static const bool enable_check_data_bound = true;
			static const bool enable_check_size_limits = true;
		};

		struct binary_archive_base_status_list
		{
			enum status
			{
				S_OK,
				S_ERROR_BAD_POS,
				S_ERROR_SIZE_OVERFLOW,
				S_ERROR_BAD_SEEK_TYPE
			};
		};

		template<bool all_disabled>
		struct binary_archive_base_status
			: public binary_archive_base_status_list
		{
			binary_archive_base_status()
				: m_status(S_OK) {}

			bool fail() const
				{ return m_status != S_OK; }

			bool good() const
				{ return m_status == S_OK; }

			void clear_status()
				{ m_status = S_OK; }

			void set_status(status s)
				{ m_status = s; }

		private:
			status m_status;
		};

		// optimize to empty class
		template<>
		struct binary_archive_base_status<true>
			: public binary_archive_base_status_list {
			bool fail() const { return false; }
			bool good() const { return true; }
			void clear_status() {}
			void set_status(status) {}
		};

		template<typename Config>
		struct binary_archive_base
			: public binary_archive_base_status<Config::disable_all>
		{
			typedef binary_archive_base_status<Config::disable_all> base_type;
			typedef Config config;

			using base_type::fail;
			using base_type::good;
			// 一旦状态设为错误，则不能继续读写，除非使用该
			// 接口将其清除
			using base_type::clear_status;

		protected:
			using base_type::set_status;
			
			inline
			bool check_data_bound(const char *begin,
					      const char *new_pos,
					      const char *end) {
				if(! config::disable_all)
				{
					if(config::enable_check_data_bound)
					{
						if((new_pos > end)
						   || (new_pos < begin))
						{
							base_type::set_status(base_type::S_ERROR_BAD_POS);
							return false;
						}
					}
				}
				return true;
			}

			// 确保size的值不超过SizeToCheck类型的最大值
			template<typename SizeToCheck, typename SizeT>
			inline
			bool check_size_limits(SizeT size) {
				if(! config::disable_all)
				{
					if(config::enable_check_size_limits)
					{
						if(size > (std::numeric_limits<SizeToCheck>::max)())
						{
							base_type::set_status(base_type::S_ERROR_SIZE_OVERFLOW);
							return false;
						}
					}
				}
				return true;
			}
		};

		template<typename IArchive, typename T>
		struct binary_iarchive_helper
		{
			static inline
			bool load(IArchive &ar, T *obj) {
				return ar.load_object(obj);
			}
		};

#define IMPL_HELPER_CLASS(integer_type)					\
		template<typename IArchive>				\
		struct binary_iarchive_helper<IArchive, integer_type>	\
		{							\
			static inline					\
			bool load(IArchive &ar, integer_type *n) {       \
				return ar.load_integer(n);		\
			}						\
		};

		IMPL_HELPER_CLASS(uint8_t);
		IMPL_HELPER_CLASS(int8_t);
		IMPL_HELPER_CLASS(uint16_t);
		IMPL_HELPER_CLASS(int16_t);
		IMPL_HELPER_CLASS(uint32_t);
		IMPL_HELPER_CLASS(int32_t);
		IMPL_HELPER_CLASS(uint64_t);
		IMPL_HELPER_CLASS(int64_t);

#undef IMPL_HELPER_CLASS

		template<typename IArchive, typename CharT, typename Traits, typename Alloc>
		struct binary_iarchive_helper< IArchive, std::basic_string<CharT, Traits, Alloc> >
		{
			typedef std::basic_string<CharT, Traits, Alloc> string_type;
			
			static inline
			bool load(IArchive &ar, string_type *str) {
				return ar.load_string(str);
			}
		};

		template<typename IArchive, typename T, typename Alloc>
		struct binary_iarchive_helper< IArchive, std::vector<T, Alloc> >
		{
			typedef std::vector<T, Alloc> vector_type;
			
			static inline
			bool load(IArchive &ar, vector_type *str) {
				return ar.load_vector(str);
			}
		};

		template<typename OArchive, typename T>
		struct binary_oarchive_helper
		{
			static inline
			bool save(OArchive &ar, const T &obj) {
				return ar.save_object(obj);
			}
		};

#define IMPL_HELPER_CLASS(integer_type)					\
		template<typename OArchive>				\
		struct binary_oarchive_helper<OArchive, integer_type>	\
		{							\
			static inline					\
			bool save(OArchive &ar, integer_type n) {       \
				return ar.save_integer(n);		\
			}						\
		};

		IMPL_HELPER_CLASS(uint8_t);
		IMPL_HELPER_CLASS(int8_t);
		IMPL_HELPER_CLASS(uint16_t);
		IMPL_HELPER_CLASS(int16_t);
		IMPL_HELPER_CLASS(uint32_t);
		IMPL_HELPER_CLASS(int32_t);
		IMPL_HELPER_CLASS(uint64_t);
		IMPL_HELPER_CLASS(int64_t);

#undef IMPL_HELPER_CLASS

		template<typename OArchive, typename CharT, typename Traits, typename Alloc>
		struct binary_oarchive_helper< OArchive, std::basic_string<CharT, Traits, Alloc> >
		{
			typedef std::basic_string<CharT, Traits, Alloc> string_type;
			
			static inline
			bool save(OArchive &ar, const string_type &str) {
				return ar.save_string(str);
			}
		};

		template<typename OArchive, typename T, typename Alloc>
		struct binary_oarchive_helper< OArchive, std::vector<T, Alloc> >
		{
			typedef std::vector<T, Alloc> vector_type;
			
			static inline
			bool save(OArchive &ar, const vector_type &str) {
				return ar.save_vector(str);
			}
		};

	} // namespace details
	
} // namespace kb

#endif	// _KB_DETAILS_BINARY_ARCHIVE_BASE_
